<?php
/**
 * 计算产品价格，优惠规则优先顺序：根据活动的优先等级，超小越优先，0为可同用多个活动
 * 优惠券可根据条件使用
 * Created by Paco
 * Date: 2018/11/29 Time: 15:01
 */

namespace App\Lib\Utils;


use App\Models\Order\Order;
use App\Models\Product\Product;
use App\Models\Product\Sku;
use App\Models\Promotion\Gift;
use App\Models\Promotion\Promotion;
use App\Models\Promotion\UserCoupon;
use App\Traits\ApiRespond;

class CartBalance
{
    use ApiRespond;
    /**
     * @var int 总价
     */
    private $total_price;

    /**
     * @var int 支付价
     */
    private $pay_price;

    /**
     * @var array 优惠券 ['coupon_id','coupon_title','coupon_price']
     */
    private $coupon;


    /**
     * @var array 促销活动[promotion_id,promotion_desc,promotion_price]
     */
    private $promotion;

    /**
     * @var int 会员ID
     */
    private $user_id;


    /**
     * @var int 物流费
     */
    private $carriage_price = 0;


    private $weight = 0;

    /**
     * @var int 增加积分
     */
    private $point = 0;

    /**
     * @var int 增加经验
     */
    private $exp = 0;

    /**
     * @var array 商家ID
     */
    private $seller_id = [0];

    /**
     * @var array  购物车数据
     */
    private $cart = [];

    /**
     * @var array 赠送礼品
     */
    private $gift = [];

    /**
     * @var int 礼品ID
     */
    private $gift_id = 0;

    /**
     * @var bool 是否为提交订单
     */
    private $submit = false;

    /**
     * @var bool 排它标识，为true后不再计算其它促销活动
     */
    private $active = false;


    public function __construct($cart, $user_id, $submit = false, $carriage_id = 0, $coupon_id = 0, $gift_id = 0)
    {
        if (count($cart) == 0) {
            $this->throwRespondError(10605);
        }
        $this->cart = $cart;
        $this->submit = $submit;
        $this->gift_id = $gift_id;

        $products = $this->getProducts();
        $this->checkStore($products);
        $this->user_id = $user_id;

        $promotions = $this->getPromotion();
        $this->productCount($promotions, $products);

        //计算优惠券
        $coupon = (new Coupon($products, $this->total_price, $coupon_id))->getCoupon();
        $this->pay_price = $this->pay_price - $coupon['reduce_price'];
        $this->coupon = $coupon['coupons'];

        //根据地址计算运费
    }

    /**
     *
     * 取得用户选择的商品信息
     * author Paco
     * createTime 2018/12/22 15:31
     * @return array
     */
    private function getProducts()
    {
        $pro_ids = [];
        $sku_ids = [];
        foreach ($this->cart as $item) {
            $pro_ids[] = $item['product_id'];
            if ($item['sku_id']) {
                $sku_ids[] = $item['sku_id'];
            }
        }

        $products = Product::query()->whereIn('product_id', $pro_ids)->get()->toArray();
        $skus = [];
        if (count($sku_ids)) {
            $skus = Sku::query()->whereIn('sku_id', $sku_ids)->get()->toArray();
        }

        $pro_data = $sku_data = $this->setIdToKey($products, 'product_id');
        $sku_data = $this->setIdToKey($skus, 'sku_id');

        foreach ($this->cart as $item) {
            if ($pro_data[$item['product_id']['disable_desc']] == 0) {
                $pro_data[$item['product_id']['sku']] = $sku_data[$item['sku_id']];
            }
        }

        return $pro_data;
    }

    private function setIdToKey($data, $id)
    {
        if (count($data)) {
            foreach ($data as $sku) {
                $data[$id] = $sku;
            }
        }
        return $data;
    }


    private function getCarriage()
    {

    }


    /**
     *
     * 检测库存
     * author Paco
     * createTime 2018/12/22 15:31
     * @param $products
     * @return mixed
     */
    private function checkStore($products)
    {
        foreach ($products as $product) {
            //判断产品是否在售，是否有库存
            if ($product['status'] == 0) {
                $this->throwRespondError(10607, null, [], ['product_name' => $product['product_name']]);
            } elseif ($product['total_stock'] <= 0) {
                $this->throwRespondError(10608, null, [], ['product_name' => $product['product_name']]);
            }

            //如果产品存在规格
            if ($product['disable_spec'] == 0) {
                $spec = json_decode($product['sku']['spec'], true);
                $str = '';
                foreach ($spec as $item) {
                    $str .= $item['value'];
                }

                $quantity = $this->cart[$product['product_id'] . '_' . $product['sku']['sku_id']]['quantity'];
                if (count($product['sku']) != 1) {
                    $this->throwRespondError(10608, null, [], ['product_name' => $product['product_name']] . $str);
                } elseif ($product['sku']['stock'] < $quantity) {
                    $this->throwRespondError(10606, null, [],
                        ['product_name' => $product['product_name'] . $str, 'stock' => $product['sku']['stock']]);
                }
                $this->pay_price = $this->total_price += $product['sku']['sku_price'];
                $this->weight += $product['sku']['sku_weight'];
            } else { //如果没有规格，则直接判断库存
                $quantity = $this->cart[$product['product_id'] . '_0']['quantity'];
                if ($product['total_stock'] < $quantity) {
                    $this->throwRespondError(10606, null, [],
                        ['product_name' => $product['product_name'], 'stock' => $product['sku']['stock']]);
                }
                $this->pay_price = $this->total_price += $product['list_price'];
                $this->weight += $product['weight'];
            }

            $this->seller_id[] = $product['seller_id'];
        }
        return $products;
    }

    /**
     *
     * 获取当前商品商家和系统活动
     * author Paco
     * createTime 2018/12/14 15:50
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    private function getPromotion()
    {
        $this->seller_id = array_unique($this->seller_id);
        return Promotion::query()->where('start_time', '<=', time())
            ->where('end_time', '>=', time())->where('is_close', 0)
            ->whereIn('seller_id', $this->seller_id)->orderBy('exclusive', 'desc')
            ->orderBy('level', 'desc')->get();
    }


    /**
     *
     * 计算订单和产品的优惠活动
     * author Paco
     * createTime 2018/12/22 15:30
     * @param $promotions
     * @param $products
     */
    private function productCount($promotions, $products)
    {
        foreach ($promotions as $promotion) {
            if ($this->active) {
                break;
            }
            if ($promotion->type == 0 || $promotion->type == 5) {
                $rs = $this->selectType($promotion);
                if ($rs && $promotion->exclusive) {
                    $this->active = true;
                }
                $this->promotion[] = $promotion;

            } else {
                foreach ($products as $k => $product) {
                    $rs = $this->selectType($promotion, $product);
                    if ($rs && $promotion->exclusive) {
                        $this->active = true;
                    }
                    $products[$k]['promotion'] = $promotion;
                }
            }
        }
    }

    /**
     *
     * 选择优惠类型
     * author Paco
     * createTime 2018/12/22 15:30
     * @param $promotion
     * @param array $product
     * @return array|bool
     */
    private function selectType($promotion, $product = [])
    {
        $result = false;
        switch ($promotion->type) {
            case 0:
                $result = $this->order($promotion);
                break;
            case 1:
                $result = $this->flashSale($promotion, $product);
                break;
            case 2:
                $result = $this->category($promotion, $product);
                break;
            case 3:
                $result = $this->special($promotion, $product);
                break;
            case 4:
                $result = $this->brand($promotion, $product);
                break;
            case 5:
                $result = $this->newUser($promotion);
                break;
        }
        return $result;
    }


    /**
     *
     * 选择优惠方式
     * author Paco
     * createTime 2018/12/22 15:29
     * @param $promotion
     * @param array $price
     * @return array|bool
     */
    private function selectAward($promotion, $price = [])
    {
        $result = [];
        switch ($promotion->award_type) {
            case 1:
                $result = $this->reducePrice($promotion->award_value);
                break;
            case 2:
                $result = $this->offPrice($promotion->award_value, $price);
                break;
            case 3:
                $result = $this->givePoints($promotion->award_value);
                break;
            case 4:
                $result = $this->giveCoupon($promotion->award_value);
                break;
            case 5:
                $result = $this->giveGift($promotion->award_value);
                break;
            case 6:
                $result = $this->freeCarriage();
                break;
            case 7:
                $result = $this->specialOffer($promotion->award_value, $price);
                break;
            case 8:
                $result = $this->giveExperience($promotion->award_value);
                break;
        }

        return $result;

    }


    /**
     *
     * 订单优惠
     * author Paco
     * createTime 2018/12/22 15:29
     * @param $promotion
     * @return bool
     */
    private function order($promotion): bool
    {
        $rs = false;
        //判断订单金额是否符合条件
        if ($this->total_price >= $promotion->condition) {
            $rs = $this->selectAward($promotion);
        }
        return $rs;
    }


    /**
     *
     * 限时抢购（秒杀）
     * author Paco
     * createTime 2018/12/22 15:28
     * @param $promotion
     * @param $product
     * @return bool
     */
    private function flashSale($promotion, $product): bool
    {
        //后台每五分钟扫一次秒杀活动，5分钟内开始的怕有产品库存提交到redis
        $rs = false;
        if (!$product) {
            return $rs;
        }

        if ($this->submit) {
            $this->flashStock($product);
        }

        $pro_ids = explode(',', $promotion->condition);
        if (!in_array($product['product_id'], $pro_ids)) {
            return $rs;
        }

        $price = $this->getProductPrice($product);

        $rs = $this->selectAward($promotion, $price);
        return $rs;
    }

    /**
     *
     * 商品分类优惠
     * author Paco
     * createTime 2018/12/22 15:28
     * @param $promotion
     * @param $product
     * @return bool
     */
    private function category($promotion, $product): bool
    {
        $rs = false;
        $cat_ids = explode(',', $promotion->condition);
        if (in_array($product['cat_id'], $cat_ids)) {
            if ($promotion->except) {
                $except_ids = explode(',', $promotion->except);
                if (in_array($product['product_id'], $except_ids)) {
                    return $rs;
                }
            }
            $price = $this->getProductPrice($product);
            $rs = $this->selectAward($promotion, $price);
        }
        return $rs;
    }

    /**
     *
     * 商品特价
     * author Paco
     * createTime 2018/12/22 15:28
     * @param $promotion
     * @param $product
     * @return array|bool
     */
    private function special($promotion, $product)
    {
        $rs = false;
        $pro_ids = explode(',', $promotion->condition);
        if (in_array($product['product_id'], $pro_ids)) {
            $price = $this->getProductPrice($product);
            $rs = $this->selectAward($promotion, $price);
        }
        return $rs;
    }

    /**
     *
     * 品牌优惠
     * author Paco
     * createTime 2018/12/22 15:28
     * @param $promotion
     * @param $product
     * @return array|bool
     */
    private function brand($promotion, $product)
    {
        $rs = false;
        $brand_ids = explode(',', $promotion->condition);
        if (in_array($product['brand_id'], $brand_ids)) {
            if ($promotion->except) {
                $except_ids = explode(',', $promotion->except);
                if (in_array($product['product_id'], $except_ids)) {
                    return $rs;
                }
            }
            $price = $this->getProductPrice($product);
            $rs = $this->selectAward($promotion, $price);
        }
        return $rs;
    }


    /**
     *
     * 新用户优惠
     * author Paco
     * createTime 2018/12/22 15:28
     * @param $promotion
     * @return array|bool
     */
    private function newUser($promotion)
    {
        $rs = false;
        //查找订单判断是否购买过商品，无则认为是新用户
        $count = Order::query()->where('user_id', $this->user_id)->count('order_number');
        if (!$count) {
            $rs = $this->selectAward($promotion);
        }
        return $rs;
    }


    /**
     *
     * 减价
     * author Paco
     * createTime 2018/12/22 15:27
     * @param $award_value
     * @return bool
     */
    private function reducePrice($award_value): bool
    {
        $rs = false;
        if ($this->pay_price > $award_value) {
            $this->pay_price = $this->pay_price - $award_value;
            $rs = true;
        }
        return $rs;
    }

    /**
     *
     * 折扣
     * author Paco
     * createTime 2018/12/22 15:27
     * @param $award_value
     * @param $price
     * @return bool
     */
    private function offPrice($award_value, $price): bool
    {
        $off_price = bcdiv(bcmul($award_value, $price), 100, 0);
        $this->pay_price = $this->pay_price - $price + $off_price;
        return true;
    }

    /**
     *
     * 赠送积分
     * author Paco
     * createTime 2018/12/22 15:27
     * @param $award_value
     * @return bool
     */
    private function givePoints($award_value): bool
    {
        $this->point = $this->point + $award_value;
        return true;
    }


    /**
     *
     * 赠送优惠券
     * author Paco
     * createTime 2018/12/22 15:26
     * @param $award_value
     * @return bool
     */
    private function giveCoupon($award_value): bool
    {
        $coupon = json_decode($award_value, true);//创建活动时把整个优惠券信息以json格式存储
        if ($coupon['start_time'] < time()) {
            $coupon['start_time'] = time();
        }
        $coupon['user_id'] = $this->user_id;
        try {
            UserCoupon::query()->create($coupon);
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }


    /**
     *
     * 赠送礼品
     * author Paco
     * createTime 2018/12/22 15:26
     * @param $award_value
     * @return bool
     */
    private function giveGift($award_value): bool
    {
        //查找礼品，并较验库存(暂不考虑并发)
        if ($this->gift_id) {
            $gift = Gift::query()->where('gift_id', $this->gift_id)
                ->where('stock', '>', 0)
                ->first();
            if (!$gift) {
                $this->throwRespondError(10609);
            }
            $this->gift = $gift;
            $gift->stock = $gift->stock - 1;
            $rs = $gift->save();
            if (!$rs) {
                $this->throwRespondError(10602);
            }
        } else {
            $gift_ids = explode(',', $award_value);
            $this->gift = Gift::query()->where('stock', '>', 0)
                ->whereIn('gift_id', $gift_ids)->get()->toArray();
        }
        return true;

    }

    /**
     *
     * 免运费
     * author Paco
     * createTime 2018/12/22 15:26
     * @return bool
     */
    private function freeCarriage(): bool
    {
        $this->carriage_price = 0;
        return true;
    }


    /**
     *
     * 特价
     * author Paco
     * createTime 2018/12/22 15:26
     * @param $award_value
     * @param $price
     * @return bool
     */
    private function specialOffer($award_value, $price): bool
    {
        $this->pay_price = $this->pay_price - $price + $award_value;
        return true;
    }


    /**
     *
     * 赠送经验
     * author Paco
     * createTime 2018/12/22 15:25
     * @param $award_value
     * @return bool
     */
    private function giveExperience($award_value): bool
    {
        $this->exp += $award_value;
        return true;
    }

    /**
     *
     * 获取商品价格
     * author Paco
     * createTime 2018/12/22 15:25
     * @param $product
     * @return mixed
     */
    private function getProductPrice($product)
    {
        if ($product['disable_spec']) {
            $price = $product['list_price'];
        } else {
            $price = $product['sku']['sku_price'];
        }
        return $price;
    }


    /**
     *
     * 秒杀判断库存
     * author Paco
     * createTime 2018/12/22 15:25
     * @param $product
     */
    private function flashStock($product)
    {
        $sku_id = 0;
        $str = '';
        if (!$product['disable_spec']) {
            $sku_id = $product['sku']['sku_id'];
            $spec = json_decode($product['sku']['spec'], true);
            foreach ($spec as $item) {
                $str .= $item['value'];
            }
        }
        $stock_key = 'stock_' . $product['product_id'] . '_' . $sku_id;
        $flash_key = 'flash_' . $product['product_id'] . '_' . $sku_id;

        $redis = app('phpredis');
        $stock = $redis->get($stock_key);

        if ($redis->lLen($flash_key) >= $stock) {
            $this->throwRespondError(10606, null, [],
                ['product_name' => $product['product_name'] . $str, 'stock' => $product['sku']['stock']]);
        }
        $redis->rPush($flash_key, $this->user_id);
    }

}